<template>
  <div>
    <!-- role角色列表组件页面 -->
    <!-- 1-Breadcrumb 面包屑:  显示当前页面的路径，快速返回之前的任意页面。 -->
    <el-breadcrumb separator-class="el-icon-arrow-right">
      <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>权限管理</el-breadcrumb-item>
      <el-breadcrumb-item>角色列表</el-breadcrumb-item>
    </el-breadcrumb>

    <!-- 2-卡片视图区 -->
    <el-card class="box-card">
      <!--2-1、 row/col栅格系统 - 添加角色按钮区域 -->
      <el-row>
        <el-col>
          <el-button type="primary" @click="showAddRoleDialog">添加角色</el-button>
        </el-col>
      </el-row>

      <!-- 2-2、主体内容区域   1-4、把角色列表数据渲染到表格中 -->
      <el-table :data="rolesList" stripe border>
        <!--（1） 展开列 -->
        <el-table-column type="expand">
          <template slot-scope="scopeProps">
            <!--(一) row栅格系统 布局  + Tag标签 + #app的min-width最小屏宽 + flex的竖向垂直-->
            <el-row
              :class="['bdbottom', 'rowcenter', i1 === 0 ? 'bdtop' : '']"
              v-for="(item1, i1) in scopeProps.row.children"
              :key="item1.id"
            >
              <!--（一、1） 渲染一级权限 ;+ 图标 ; + Tag标签及其可移除标签closable + @close:关闭 Tag 时触发的事件-->
              <el-col :span="5">
                <el-tag @close="removeRightById(scopeProps.row, item1.id)">{{
                  item1.authName
                }}</el-tag>
                <i class="el-icon-caret-right"></i>
              </el-col>
              <!--（一、2） 渲染二级\三级权限 -->
              <el-col :span="19">
                <!-- 通过for循环嵌套 渲染二级权限 -->
                <el-row
                  :class="[i2 === 0 ? '' : 'bdtop', 'rowcenter']"
                  v-for="(item2, i2) in item1.children"
                  :key="item2.id"
                >
                  <el-col :span="6">
                    <el-tag
                      closable
                      type="success"
                      @close="removeRightById(scopeProps.row, item2.id)"
                      >{{ item2.authName }}</el-tag
                    >
                    <i class="el-icon-caret-right"></i>
                  </el-col>
                  <!-- 5、通过for循环嵌套 渲染三级权限 +@close关闭事件，弹出框询问是否根据id删除角色指定权限 -->
                  <el-col :span="18">
                    <el-tag
                      closable
                      type="warning"
                      v-for="item3 in item2.children"
                      :key="item3.id"
                      @close="removeRightById(scopeProps.row, item3.id)"
                      >{{ item3.authName }}</el-tag
                    >
                    <!-- scopeProps.row 当前行的所有数据  = role.chlidren-->
                  </el-col>
                </el-row>
              </el-col>
            </el-row>
          </template>
        </el-table-column>
        <!--（2） 索引列 ，各项渲染列-->
        <el-table-column type="index" label="#"></el-table-column>
        <el-table-column prop="roleName" label="角色名称"></el-table-column>
        <el-table-column prop="roleDesc" label="角色描述"> </el-table-column>
        <el-table-column label="操作" width="350">
          <!-- 作用域插槽; 获取当前行的数据 ； + 图标按钮 + Tooltip 文字提示 -->
          <template slot-scope="scope">
            <!-- 编辑按钮 -->
            <el-button type="primary" icon="el-icon-edit" size="mini" @click='showEditRoleDialogVisible(scope.row.id)'
              >编辑</el-button
            >
            <!-- 删除按钮 点击按钮，根据当前用户角色的Id删除对应的角色信息-->
            <el-button
              type="danger"
              icon="el-icon-delete"
              size="mini"
              @click="removeUserById(scope.row.id)"
              >删除</el-button
            >
            <!-- 6、分配权限按钮 6、（7）scope.row当前角色所有信息数据-->
            <el-button
              type="warning"
              icon="el-icon-setting"
              size="mini"
              @click="showSetRightDialog(scope.row)"
              >分配权限</el-button
            >
          </template>
        </el-table-column>
      </el-table>

      <!-- 3、分页 没有接口，暂时不做-->

    </el-card>

    <!-- 6、分配权限按钮的Dialog对话框  + Tree树形控件 +监听对话框关闭事件，清空-->
    <el-dialog
      title="分配权限"
      :visible.sync="SetRightDialogVisible"
      width="50%"
      @close="SetRightDialogClosed"
    >
      <!--6、(5)tree树形控件：用清晰的层级结构展示信息，可展开或折叠。
       show-checkbox:复选框，需要选择层级时使用； 
       default-expand-all，是否默认展开所有节点
       node-key，其每个树节点用来作为唯一标识的属性，整棵树应该是唯一的。
       default-checked-keys	, 默认勾选的节点的 key 的数组	array
       default-expanded-keys, 默认展开的节点的 key 的数组 -->
      <!-- 难点：加载当前角色已有权限，默认勾选已有权限
        ①页面中定义一个递归函数，把角色信息传递给递归函数中，②通过递归的形式，把所有三级节点的Id都保存到一个数组内，
        ③再把这个数组赋值给defKey:[],默认勾选
    
     -->
      <el-tree
        :data="rightsList"
        :props="treeProps"
        show-checkbox
        node-key="id"
        :default-expand-all="true"
        :default-checked-keys="defKey"
        ref="treeRef"
      ></el-tree>
      <span slot="footer" class="dialog-footer">
        <el-button @click="SetRightDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="allotRights">确 定</el-button>
      </span>
    </el-dialog>

   <!-- 2-1、添加角色弹出框 -->
  <el-dialog
  title="添加角色"
  :visible.sync="addRoleDialogVisible"
  width="50%" @close='addRoleDialogClosed'>
  
  <!-- 主体内容--form 表单 -->
  <el-form :model="addRoleForm" :rules="addRoleFormRules" ref="addRoleFormRef" label-width="80px" class="fm">
  <el-form-item label="角色名称" prop="roleName">
    <el-input v-model="addRoleForm.roleName"></el-input>
  </el-form-item>
  <el-form-item label="角色描述" prop="roleDesc">
    <el-input v-model="addRoleForm.roleDesc"></el-input>
  </el-form-item>

  </el-form>
  <span slot="footer" class="dialog-footer">
    <el-button @click="addRoleDialogVisible = false">取 消</el-button>
    <el-button type="primary" @click="addRole">确 定</el-button>
  </span>
  </el-dialog>

  <!-- 3、控制修改-编辑角色按钮弹出框 -->
<el-dialog
  title="修改角色"
  :visible.sync="editRoleDialogVisible"
  width="50%" @close='editRoleDialogClose'>
  <!-- form表单主体区域 -->
<el-form :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="100px">
  <el-form-item label="角色名称" prop="roleName">
    <el-input v-model="editForm.roleName"></el-input>
  </el-form-item>
    <el-form-item label="角色描述" prop="roleDesc">
    <el-input v-model="editForm.roleDesc"></el-input>
  </el-form-item>
</el-form>
  <span slot="footer" class="dialog-footer">
    <el-button @click="editRoleDialogVisible = false">取 消</el-button>
    <el-button type="primary" @click="editRole">确 定</el-button>
  </span>
</el-dialog>





  </div>
  <!-- 重点1：展开列
通过设置 type="expand" 和 Scoped slot 可以开启展开行功能，el-table-column 的模板会被渲染成为展开行的内容，展开行可访问的属性与使用自定义列模板时的 Scoped slot 相同。 -->

  <!--被包围在 pre 元素中的文本通常会保留空格和换行符,用来表示计算机的源代码。 -->
  <!-- 重点2：Layout 布局，通过基础的 24 分栏，迅速简便地创建布局，el-row栅格响应 -->
  <!-- 重点3：Tag标签 
(1)可移除标签:设置closable属性可以定义一个标签是否可移除。默认的标签移除时会附带渐变动画，如果不想使用，可以设置disable-transitions属性，它接受一个Boolean，true 为关闭。
(2)click	点击 Tag 时触发的事件
(3)close	关闭 Tag 时触发的事件
-->
</template>


<script>
export default {
   name: "Roles",
  data() {
    return {
      //存储获取到的所有角色列表数据
      rolesList: [],

      //2-1、控制添加角色按钮，弹出框的显示与隐藏
       addRoleDialogVisible:false,
    
      //2-2、这是添加-用户角色的表单数据绑定对象
      //添加-用户角色信息的表单数据
        addRoleForm:{
         roleName:'',
         roleDesc:''
       },

      //2-3、为填写的表单数据进行验证----添加表单的验证规则对象
      addRoleFormRules:{
        roleName: [
            { required: true, message: '请输入角色名称', trigger: 'blur' },
            { min: 3, max: 5, message: '长度在 2 到 10 个字符', trigger: 'blur' }
          ],
         roleDesc: [
            { required: false, message: '请输入角色名称', trigger: 'blur' },
            { min: 3, max: 35, message: '长度在 2 到 10 个字符', trigger: 'blur' }
          ],
      },

      //3、控制修改-编辑角色按钮，弹出框的显示与隐藏
      editRoleDialogVisible :false,
      //3-1、保存根据 ID 查询到的当前角色信息
      editForm: {},
      //3-2、为填写的修改表单数据进行验证----添加表单的验证规则对象
      editFormRules:{
          roleName: [
            { required: true, message: '请输入角色名称', trigger: 'blur' },
            { min: 3, max: 5, message: '长度在 2 到 10 个字符', trigger: 'blur' }
          ],
         roleDesc: [
            { required: false, message: '请输入角色名称', trigger: 'blur' },
            { min: 3, max: 35, message: '长度在 2 到 10 个字符', trigger: 'blur' }
          ],
      },
      //6、(1) 控制分配权限对话框的显示与隐藏
      SetRightDialogVisible: false,

      //6、(2) 保存获取到的所有分配权限数据列表
      rightsList: [],

      //6、(5) Tree属性控件属性绑定对象
      treeProps: {
        label: "authName",
        children: "children",
      },
      //默认勾选的节点的Id值的数组:default-checked-keys=''
      defKey: [],

      //6、（10）打开分配权限对话框后， 即将分配权限的当前角色ID
      roleId: "",
    };

  },
  created() {
    //生命周期创建阶段：created--在初始化vue实例上的属性和方法之后 执行； created: function ()
    //1、一打开页面，立即调用获取渲染所有的角色列表数据
    this.getRolesList();
  },
  methods: {
    //1、发起请求，获取所有的角色数据列表
    async getRolesList() {
      const ret = await this.$http.get("roles");
      const res = ret.data;
      // console.log(ret);
      console.log(res);
      //(2) 判断是否成功获取 角色数据列表
      if (res.meta.status !== 200) {
        return this.$message.error("获取角色数据列表失败！ " + res.meta.msg);
      }
      this.$message.success("获取角色数据列表成功！");
      //(3)存储获取到的角色数据列表, 赋值给 data 中的 rolesList[]空数组存储
      this.rolesList = res.data;
    },

    //2、点击el-row中的添加角色信息按钮，显示对话框
    showAddRoleDialog(){   
      this.addRoleDialogVisible=true
    },
    //2-4、监听添加角色对话框的关闭事件--ref标记属性，得到表单实例==>调用表单重置方法resetFields
    addRoleDialogClosed(){
      this.$refs.addRoleFormRef.resetFields()
    },
    //2-5 点击el-dialog内的确定按钮，添加新角色
     addRole(){
      //(1)点击确定按钮，提交前，先进行预校验表单数据是否合法
      this.$refs.addRoleFormRef.validate(async (valid)=>{
        console.log(valid);
        //根据校验结果，来决定是否发起网络请求
        if(!valid){
          return this.$message.error('您的填写不符合要求')
        }
        //预校验通过，可以发起网络请求
        const ret = await this.$http.post('roles',this.addRoleForm)
        const res = ret.data
        console.log(res);
        //判断是否添加成功
        if(res.meta.status!==201){
          return this.$message.error('添加角色失败！'+res.meta.msg)
        }
        this.$message.success('添加角色信息成功！')

        //点击确定按钮，①提交数据，进行验证；②关闭对话框
        this.addRoleDialogVisible = false

        //重新获取所有的角色数据列表
         this.getRolesList()
      })
      
    },

    //3、修改角色信息
    //3-1、展示：修改编辑-角色信息对话框
    async showEditRoleDialogVisible(id){
      console.log('当前行的角色id:' + id);
    //根据 ID 查询当前角色信息--默认填充到表单中
    const ret = await this.$http.get('roles/'+id)
    const res = ret.data
    console.log(res);

    //判断是否成功获取当前角色信息
    if(res.meta.status!==200){
      return this.$message.error('获取当前角色数据失败！'+res.meta.msg)
    }
    this.$message.success('获取当前角色数据成功！')
    
    //把获取到的当前角色信息保存到data数据中，--默认填充到表单中
    this.editForm=res.data
    console.log(this.editForm);
      //显示对话框
      this.editRoleDialogVisible = true
    },

    //3-2、监听：修改角色信息对话框的关闭事件-重置表单，不在显示上一次的验证结果
    editRoleDialogClose(){
      this.$refs.editFormRef.resetFields()
    },
    //3-3、提交当前行修改的角色信息--确定提交前，先进行预校验；关闭修改框
    editRole(){
      //点击确定按钮，提交前，先校验是否符合要求
      this.$refs.editFormRef.validate(async(valid)=>{
        console.log('验证修改角色信息的结果:'+valid);
        //根据验证结果决定是否发起请求，提交修改后的数据
        if(!valid){
          return this.$message.error('你的填写不符合要求')
        }
        //验证通过，允许发起网络请求，提交数据
        const ret = await this.$http.put('roles/' + this.editForm.roleId,{
          roleName:this.editForm.roleName,
          roleDesc:this.editForm.roleDesc
          })
        const res = ret.data
        console.log(res);
        
        //判断是否修改提交成功
        if(res.meta.status!==200){
          return this.$message.error('提交修改角色信息失败！'+res.meta.msg)
        }
        this.$message.success('提交修改角色信息成功！')
        //刷新-重新获取-渲染角色信息列表
        this.getRolesList()

        //确定，关闭修改对话框
        this.editRoleDialogVisible = false
        //刷新-重新获取-渲染角色信息列表
        this.getRolesList()        
      })
    },

    //4、根据当前用户角色的Id删除对应的角色信息--MessageBox 弹框
    //分页--解决当前页只有一条数据时，此数据删除后，不显示前一页内容，除非手动点击
    async removeUserById(id) {
      //scope.row.id==>id
      console.log('当前用户角色的Id'+id);
    //弹出询问用户是否删除数据的对话框--MessageBox 弹框
      const confirmResult =await this.$confirm('此操作将永久删除该角色, 是否继续?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).catch((error)=>{
          return error
        })
      console.log(confirmResult);
    //用户点击取消，打印字符串cancel
    //用户点击确定，打印字符串confirm

    //根据用户的点击判断是否删除当前角色
    if(confirmResult!=='confirm'){
      return this.$message.info('已取消删除')
    }
    //点击确定删除，发起请求
    const ret = await this.$http.delete('roles/'+id)
    const res = ret.data
    console.log(res);
    //判断是否成功删除当前角色
    if(res.meta.status!==200){
      return this.$message.error('删除当前角色失败!'+res.meta.msg)
    }
    this.$message.success('已成功删除当前角色！')
    //刷新-重新获取-渲染角色信息列表
    this.getRolesList() 

      //bug:解决当前页只有一条数据时，此数据删除后，不显示前一页内容，除非手动点击
      //一、原因：
      //最后一页的最后一条数据，删除成功后，此时还在当前页，页码值没变,但此时当前页已经没有数据了，
      //同时请求刷新数据，请求对应页即最后一页的数据，故是空白，没有数据显示
      //二、解决：
      //判断当前页即最后一页，是否还有剩余的数据；
      //若没有数据了，则当前页面值 -1，
      //重新发请求刷新，拿到前一页的数据


    },
    

    //5、三级权限 +Tag标签的@close关闭事件，MessageBox 弹框询问是否根据id删除对应权限
    async removeRightById(role, rightId) {
      console.log(role); //角色数据
      console.log(rightId); //权限id
      //(1) MessageBox 弹框询问是否根据id删除对应权限
      const confirmResult = await this.$confirm(
        "此操作将永久删除该权限, 是否继续?",
        "提示",
        {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        }
      ).catch((error) => {
        //捕获用户的取消行为
        return error;
      });
      //确定删除，打印 字符串cofirm
      //取消删除，打印 字符串cancel
      console.log(confirmResult);
      //(2) 判断用户是否想要删除
      if (confirmResult !== "confirm") {
        return this.$message.info("已取消删除！");
      }

      //(3) 发起请求，删除删除角色指定权限，同步到后台数据，重新渲染
      const { data: res } = await this.$http.delete(
        `roles/${role.id}/rights/${rightId}`
      );
      console.log(res);
      if (res.meta.status !== 200) {
        return this.$message.error("取消权限失败！ " + res.meta.msg);
      }
      this.$message.success("已成功删除该权限！");
      //(4) 此时不推荐调用- 会完全重新刷新页面，把展开列关闭
      // this.getRolesList();
      // 为role角色id的那条数据重新赋值,渲染页面
      // scopeProps.row 当前删除行row的所有数据  ==> res.data ==>  role.chlidren
      role.children = res.data;
    },

    //6.分配权限按钮---展示分配权限Dialog 对话框
    async showSetRightDialog(role) {
      //(scope.row==>role当前角色所有信息数据
      //当前角色的id
      this.roleId = role.id;
      console.log(role);
      //(1、2)获取所有的分配权限数据列表-- tree 树状显示权限
      const ret = await this.$http.get("rights/tree");
      const res = ret.data;
      console.log(res);

      //(3)判断是否获取数据成功
      if (res.meta.status !== 200) {
        return this.$message.error("获取权限数据列表失败！" + res.meta.msg);
      }
      this.$message.success("获取权限数据列表成功！");
      //(4)把获取到的权限数据列表 存储到 data
      this.rightsList = res.data;
      console.log(this.rightsList);

      //(5)把获取到的数据在对话框中渲染出来--树形控件Tree实现

      //6、(7)递归获取三级节点的Id，保存到data数据中
      this.getLeafKeys(role, this.defKey);

      //(8)注册点击事件，点击分配权限按钮，弹出Dialog 对话框，在弹出前，获取并渲染所有数据
      this.SetRightDialogVisible = true;
    },
    //6、(6)定义递归函数，通过递归的形式，获取当前角色下所有三级权限的id,并保存到 defKeys数组内，来默认勾选
    getLeafKeys(node, arr) {
      //node：被递归的对象，判断该节点是否是三级权限节点
      //arr：保存三级节点的Id的数组容器
      //①通过是否具有children属性，判断是否是三级权限节点
      if (!node.children) {
        //当前node节点不包含children属性，则是三级节点，保存到数组中
        return arr.push(node.id);
      }
      //②没有return,还不是三级节点，需要调用递归获取到最终的三级节点
      //③循环当前node里面的所有数组,
      node.children.forEach((item) => {
        this.getLeafKeys(item, arr);
      });
    },

    //bug:点击获取当前角色权限id并默认勾选后，关闭此角色；再次点击下个角色时，原先的角色权限数组数据也会保存到新打开的角色权限数组数据内。
    //旧：3，新：2+3=5
    //解决：监听当前角色对话框Dialog点击关闭事件，清空当前角色权限数组内的数据
    //6、（9）监听对话框的关闭事件
    SetRightDialogClosed() {
      //点击关闭，清空当前角色权限数组内的数据
      this.defKey = [];
    },

    //6、（10）点击，为当前角色分配添加、删除最新权限
    async allotRights() {
      //① 得到页面中当前角色，用户选中的权限id(全选中/子ID--半选中/父ID)
      //es6,扩展运算符提供了数组合并
      const keys = [
        ...this.$refs.treeRef.getCheckedKeys(),
        ...this.$refs.treeRef.getHalfCheckedKeys(),
      ];
      // console.log(keys);
      //权限 ID 列表（字符串）
      const idStr = keys.join(",");
      console.log(idStr);

      // ②发起post请求,提交-存储-更新用户重新选中或删掉后的数据  roleId
      const ret = await this.$http.post(`roles/${this.roleId}/rights`, {
        rids: idStr,
      });
      const res = ret.data;
      console.log(res);
      //③判断是否提交---分配权限成功
      if (res.meta.status !== 200) {
        return this.$message.error("分配权限失败！" + res.meta.msg);
      }
      this.$message.success("为当前角色分配权限成功！");
      //④重新刷新权限数据列表，渲染页面
      this.getRolesList();
      //③隐藏关闭对话框
      this.SetRightDialogVisible = false;
    },



    //用户列表Users组件中有一个  分配角色  的设置， 此role角色列表组件

  },








}
</script>





<style lang="less" scoped>
.el-tag {
  margin: 7px;
}
.bdtop {
  border-top: 1px solid #eee;
}
.bdbottom {
  border-bottom: 1px solid #eee;
}
.rowcenter {
  display: flex;
  align-items: center;
}
.fm{
  padding: 30px 20px !important;
}
</style>